Skip to main content

Cargar Archivos en Node

DigitalOcean, utilizando la infraestructura de S3 de Amazon, permite la manipulación de archivos (cargar, ver y eliminar) de manera eficiente. Con Node.js, esta manipulación puede ser optimizada y resulta fácil de configurar y utilizar.

Paquetes a utilizar:

npm install @aws-sdk/client-s3
npm install express-fileupload

Variables de entorno:

S3_ENDPOINT
BUCKET_NAME
BUCKED_ENDPOINT
AWS_ACCESS_KEY
AWS_SECRET_ACCESS_KEY
AWS_REGION
  1. S3_ENDPOINT

    • Descripción: URL del servidor para acceder a los servicios de almacenamiento en la nube (como Amazon S3 o DigitalOcean Spaces).
    • Ejemplo: https://nyc3.digitaloceanspaces.com
  2. BUCKET_NAME

    • Descripción: Nombre del contenedor donde se almacenan los archivos.
    • Ejemplo: my-app-bucket
  3. BUCKED_ENDPOINT

    • Descripción: URL base para acceder a los archivos en el bucket.
    • Ejemplo: https://my-app-bucket.nyc3.digitaloceanspaces.com
  4. AWS_ACCESS_KEY

    • Descripción: Clave pública para autenticar solicitudes a AWS.
    • Ejemplo: AKIAIOSFODNN7EXAMPLE
  5. AWS_SECRET_ACCESS_KEY

    • Descripción: Clave secreta para firmar solicitudes a AWS.
    • Ejemplo: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
  6. AWS_REGION

    • Descripción: Región geográfica de AWS donde está el bucket.
    • Ejemplo: us-east-1

Con estos paquetes y variables, crearemos un controlador para la carga de archivos, un servicio para conectar con DigitalOcean y manipular dichos archivos, así como las rutas necesarias.

No olvides agregar tu configuración de express-fileupload

La carga de archivos se encuentra disponible en el esqueleto de node-mongo.

Cómo Empezar

El proyecto se divide en tres componentes:

router service controller

Upload.route.js

Este archivo define las rutas y validaciones para las operaciones de carga, eliminación y obtención de archivos.

// upload.validations.js

const { query, body } = require("express-validator");

const validationFolderQuery = [
query("folder").notEmpty().isString().withMessage("It is required")
]

const validationDeleteQuery = [
query("file").notEmpty().isString().withMessage("It is required")
]

const validationDeleteMultiple = [
query("folder").notEmpty().isString().withMessage("It is required"),
body("files").notEmpty().isArray().withMessage("It is required")
]

module.exports = {
validationFolderQuery,
validationDeleteQuery,
validationDeleteMultiple
};

// upload.route.js
const { Router } = require("express");
const { uploadFile, deleteArrayFile, deleteFile, getTodoFiles } = require("./upload.controller");
const { validationFolderQuery, validationDeleteQuery, validationDeleteMultiple } = require("./upload.validations");
const { validateMiddlewareChecks } = require("../../../middleware/validation.middleware");

const routes = Router();

routes.post(
"/uploadFile",
[
...validationFolderQuery,
validateMiddlewareChecks
],
uploadFile
)

routes.delete(
"/deleteFile",
[
...validationFolderQuery,
...validationDeleteQuery,
validateMiddlewareChecks
],
deleteFile
)


routes.get(
"/getFiles",
[
...validationFolderQuery,
validateMiddlewareChecks
],
getTodoFiles
)

routes.post(
"/deleteArrayFiles",
[
...validationDeleteMultiple,
validateMiddlewareChecks
],
deleteArrayFile
)

module.exports = routes;

Upload.controller.js

El controlador se encarga de procesar las solicitudes y devolver las respuestas necesarias para el frontend.

// upload.controller.js
const { response } = require("express");
const { getSuccessfulResponse, errorHandler } = require("../../../helpers/responses");
const { fileUpload, fileDeleted, arrayFilesDeleted, getAllFiles } = require("./upload.service")

const pathFolder = 'projectName/folder'

const uploadFile = async (req, res = response) => {
if (!req.files || !req.files.file) return res.json({ msg: "No files uploaded" });
const { folder } = req.query;
const { file } = req.files;


try {
const url = await fileUpload(file, folder);
return getSuccessfulResponse(res, { status: 0, msg: "File was uploaded successfully", payload: url });

} catch (error) {
return errorHandler({ res, message: error.message, path: req.originalUrl })
}
}

const deleteFile = async (req, res = response) => {
const { file, folder } = req.query;

try {
const url = await fileDeleted(file, folder);
return getSuccessfulResponse(res, { status: 0, msg: "File was delete successfully", payload: url });

} catch (error) {
return errorHandler({ res, message: error.message, path: req.originalUrl })
}
}

const deleteArrayFile = async (req, res = response) => {
const { folder } = req.query;
const { files } = req.body;

try {
const objects = files.map(key => ({ Key: `${folder}/${key}` }));

const arrayFile = await arrayFilesDeleted(objects);

return getSuccessfulResponse(res, { status: 0, msg: "File was delete successfully", payload: arrayFile });

} catch (error) {
return errorHandler({ res, message: error.message, path: req.originalUrl })
}
}

const getTodoFiles = async (req, res = response) => {
const { folder } = req.query;

const data = await getAllFiles(folder)

const filesByFolder = data.map(({ Key, LastModified, Size }) => ({
Key,
LastModified,
Size
}));

return getSuccessfulResponse(res, { status: 0, msg: "Gets Files", payload: filesByFolder });
}

module.exports = {
uploadFile,
deleteFile,
getTodoFiles,
deleteArrayFile
}

Upload.service.js

El servicio se encarga de interactuar con AWS S3 o DigitalOcean Spaces para realizar las operaciones de carga, eliminación y obtención de archivos.

//upload.service.js
const { S3Client, PutObjectCommand, DeleteObjectCommand, DeleteObjectsCommand, ListObjectsV2Command } = require("@aws-sdk/client-s3");

const s3 = new S3Client({
endpoint: process.env.S3_ENDPOINT, // Configura el endpoint (puede ser AWS o DigitalOcean Spaces)
region: process.env.AWS_REGION, // Especifica la región si es necesario
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY
}
});


const fileUpload = async (file, path) => {
const keyName = `${path}/${Date.now().toString()}-${file.name}`;

const uploadCommand = new PutObjectCommand({
ACL: "public-read",
Bucket: process.env.BUCKET_NAME,
Key: keyName,
Body: file.data
});

await s3.send(uploadCommand);

const urlFile = `${process.env.BUCKED_ENDPOINT}/${keyName}`;

return urlFile;
};


const fileDeleted = async (file, path) => {
const keyName = `${path}/${file}`;

const deleteCommand = new DeleteObjectCommand({
Bucket: process.env.BUCKET_NAME,
Key: keyName
});

await s3.send(deleteCommand);

const urlFile = `${process.env.BUCKED_ENDPOINT}/${keyName}`;

return urlFile;
};

const arrayFilesDeleted = async (objects = []) => {

const deleteCommand = new DeleteObjectsCommand({
Bucket: process.env.BUCKET_NAME,
Delete: { Objects: objects }
});

const { Deleted } = await s3.send(deleteCommand);

return Deleted;
};

const getAllFiles = async (folder) => {

const listCommand = new ListObjectsV2Command({
Bucket: process.env.BUCKET_NAME,
Prefix: folder
});

const { Contents } = await s3.send(listCommand);

return Contents;
};


module.exports = {
fileUpload,
fileDeleted,
arrayFilesDeleted,
getAllFiles,
}